package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func (this *User) ReflectCallFuncHasArgs(name string, id int) {
	fmt.Printf("ReflectCallFuncHasArgs: name:%s id:%d, original-> name:%s id:%d\n", name, id, this.Name, this.Id)
}

func (this *User) ReflectCallFuncNoArgs() {
	fmt.Printf("ReflectCallFuncNoArgs: original-> name:%s id:%d\n", this.Name, this.Id)
}

func doFieldAndMethod(input interface{}) {
	var getType = reflect.TypeOf(input)
	var getValue = reflect.ValueOf(input)

	fmt.Printf("get type is:%s\n", getType.Name())
	fmt.Printf("get value is:%v\n", getValue.Interface())

	// get field
	if getType.Kind() == reflect.Struct {
		for i := 0; i < getType.NumField(); i++ {
			field := getType.Field(i)
			value := getValue.Field(i).Interface()
			fmt.Printf("%s %v %v\n", field.Name, field.Type, value)
		}
	}

	// get method
	for i := 0; i < getType.NumMethod(); i++ {
		m := getType.Method(i)
		fmt.Println(m.Name, m.Type)
	}

}

func main() {
	var num float64 = 1.2345
	fmt.Println(reflect.ValueOf(num).Interface())
	fmt.Println(reflect.TypeOf(num).Name())

	u := User{1, "xx", 12}
	doFieldAndMethod(u)

	// modify value whose type is unknow
	// the method must by pointer
	for {
		pointer := reflect.ValueOf(&num)
		if pointer.Kind() != reflect.Ptr {
			panic("value's kind error")
		}
		newValue := pointer.Elem()

		fmt.Printf("type of pointer: %v\n", newValue.Type())
		fmt.Printf("type of pointer: %v\n", newValue.CanSet())

		//newValue.SetFloat(2.444444444)
		newValue.Set(reflect.Value(reflect.ValueOf(2.444444444)))
		fmt.Println(num)

		break
	}

	for {
		getValue := reflect.ValueOf(&u)

		// call method which has args by reflect
		methodValue := getValue.MethodByName("ReflectCallFuncHasArgs")
		if methodValue.Kind() != reflect.Method {
			panic("value's kind error")
		}

		fmt.Println(methodValue)
		args := []reflect.Value{reflect.ValueOf(string("reflect")), reflect.ValueOf(int(11111))}
		methodValue.Call(args)

		// call method which has no args by reflect
		methodValue = getValue.MethodByName("ReflectCallFuncNoArgs")
		if methodValue.Kind() != reflect.Method {
			panic("value's kind error")
		}

		args = []reflect.Value{}
		methodValue.Call(args)
		break
	}

}
